"""
MCPy编译器

MCPy编译器的主要实现，负责协调各个组件完成Python到C++的转换。
"""

import subprocess
import ast
import warnings
from pathlib import Path
from typing import List, Dict, Any, Optional
from dataclasses import dataclass
from .ir.declarations.import_node import IRImportNode, ImportKind


from .ir import IRModule
from .generator import CodeGenerator
from .passes import get_default_passes
from .passes.base import (
    BasePass,
    PassContext,
    PASS_STAGES,
    run_passes_in_stage,
    run_extensions_in_stage,
)
from . import extensions  # noqa: F401
from .extension_base import get_global_extension_registry
from .config import get_default_config
from .stubs_manager import configure_stubs, initialize_stubs
from .module_manager import ModuleManager
from .comment_parser import CommentParser


@dataclass
class CompilationResult:
    """编译结果"""

    success: bool
    output_files: List[str]
    errors: List[str]
    generated_contents: Optional[Dict[str, str]] = None
    ir_module: Optional[IRModule] = None


class MCPyCompiler:
    """MCPy编译器"""

    def __init__(self, config: Optional[Dict[str, Any]] = None):
        # 如果没有提供配置，使用默认配置
        if config is None:
            self.config = get_default_config()
        else:
            # 合并用户配置和默认配置
            default_config = get_default_config()
            self.config = {**default_config, **config}
            if "compiler" in config:
                self.config["compiler"] = {
                    **default_config.get("compiler", {}),
                    **config["compiler"],
                }

        # 初始化组件
        self.code_generator = CodeGenerator()
        self.module_manager = None  # 延迟加载ModuleManager

        # 配置stubs管理器，将stubs目录添加到Python路径
        configure_stubs(self.config)
        initialize_stubs()

    def compile_file(self, file_path: str | Path) -> CompilationResult:
        """编译单个文件"""
        try:
            # 确保file_path是Path对象
            if isinstance(file_path, str):
                file_path = Path(file_path)

            # 如果没有设置 project_name，从文件名推导
            if "project_name" not in self.config or self.config["project_name"] is None:
                self.config["project_name"] = file_path.stem

            with open(file_path, "r", encoding="utf-8") as f:
                source_code = f.read()
            if self.config.get("verbose"):
                print(f"解析 AST: {file_path}")

            # 抑制 finally 块中的 return/break/continue 警告（这些是预期的测试用例）
            with warnings.catch_warnings():
                warnings.filterwarnings("ignore", category=SyntaxWarning)
                raw_ast = ast.parse(source_code, filename=str(file_path))
            result = self._compile_internal(raw_ast, file_path, source_code)

            return result
        except Exception as e:
            import traceback

            error_msg = f"{str(e)}\n{traceback.format_exc()}"
            return CompilationResult(False, [], [error_msg], generated_contents=None)

    def compile_source(
        self, source_code: str, file_name: str = "<string>"
    ) -> CompilationResult:
        """编译源代码字符串"""

        try:
            # 抑制 finally 块中的 return/break/continue 警告（这些是预期的测试用例）
            with warnings.catch_warnings():
                warnings.filterwarnings("ignore", category=SyntaxWarning)
                raw_ast = ast.parse(source_code, filename=file_name)
            return self._compile_internal(
                raw_ast, module_path=Path("."), source_code=source_code
            )
        except Exception as e:
            import traceback

            error_msg = f"{str(e)}\n{traceback.format_exc()}"
            return CompilationResult(
                False, [], [error_msg], generated_contents=None, ir_module=None
            )

    def _compile_internal(
        self, raw_ast, module_path: Path, source_code: str
    ) -> CompilationResult:
        """构建 IR、运行 Pass、生成代码、可选保存并返回内容。"""
        try:
            # 构建 IR
            ir_module = self._build_ir(
                raw_ast, module_path=module_path, source_code=source_code
            )

            # 获取 Pass 列表
            custom_passes = self.config.get("custom_passes")
            if custom_passes is not None:
                passes = custom_passes
            else:
                passes = get_default_passes(self.config)

            pass_context = PassContext(compiler=self)

            ir_module = self._run_staged_compilation(passes, ir_module, pass_context)

            # 仅运行 Pass
            if self.config.get("passes_only"):
                return CompilationResult(
                    True, [], [], generated_contents={}, ir_module=ir_module
                )

            # 生成代码
            generated_files = self._generate_code(ir_module)

            # 格式化代码
            if self.config.get("format_code"):
                generated_files = self._format_generated_contents(generated_files)

            return CompilationResult(
                True,
                list(generated_files.keys()),
                [],
                generated_contents=generated_files,
                ir_module=ir_module,
            )

        except Exception as e:
            return self._handle_compilation_failure(e)

    def _build_ir(self, raw_ast, module_path: Path, source_code: str) -> Any:
        """构建 IR 模块"""
        module_name = self.config.get("project_name", "main")
        ir_module = IRModule(module_full_name=module_name, module_path=module_path)
        ir_module.build_from_ast(raw_ast)
        self._parse_comments(raw_ast, ir_module, source_code)
        self._set_source_root(ir_module)

        return ir_module

    def _parse_comments(self, raw_ast, ir_module: IRModule, source_code: str) -> None:
        """解析注解并智能绑定到 IR 模块中"""

        parser = CommentParser()
        parser.parse_comments(source_code, ir_module, raw_ast)

    def _set_source_root(self, ir_module: IRModule) -> None:
        """设置 IR 模块的源文件根目录"""
        source_root = self.config.get("source_root", ".")
        ir_module.source_root = Path(source_root).resolve()

    def _run_staged_compilation(
        self, passes: List[BasePass], ir_module: IRModule, pass_context: PassContext
    ) -> IRModule:
        """运行阶段化编译流程

        Args:
            passes: Pass 列表
            ir_module: IR 模块
            pass_context: Pass 上下文

        Returns:
            处理后的 IR 模块
        """

        extension_registry = get_global_extension_registry()
        current_module = ir_module

        # 在 pre_analysis 阶段之前，先解析所有导入语句并加载扩展模块
        # 这样 pre_analysis 阶段的扩展处理器就能找到所有已注册的处理器
        self._resolve_all_imports(ir_module)

        # 逐阶段执行
        for stage in PASS_STAGES:
            # 1. 运行阶段前的扩展
            current_module = run_extensions_in_stage(
                stage, "pre", current_module, extension_registry, compiler=self
            )

            # 2. 运行该阶段的 Pass
            current_module = run_passes_in_stage(
                passes, stage, current_module, pass_context
            )

            # 3. 运行阶段后的扩展
            current_module = run_extensions_in_stage(
                stage, "post", current_module, extension_registry, compiler=self
            )

        return current_module

    def _resolve_all_imports(self, ir_module: IRModule) -> None:
        """在 pre_analysis 阶段之前解析所有导入语句，确保扩展模块被加载

        这样 pre_analysis 阶段的扩展处理器就能找到所有已注册的处理器。

        Args:
            ir_module: IR 模块
        """

        for node in ir_module.walk():
            if not isinstance(node, IRImportNode):
                continue

            if node.is_resolved:
                continue

            node.resolve_import(self)

    def _generate_code(self, ir_module: IRModule) -> Dict[str, str]:
        """生成代码"""
        try:
            # 从配置中获取模块名称
            module_name = self.config.get("project_name")
            return self.code_generator.generate_from_ir(
                ir_module, module_name=module_name
            )
        except Exception as e:
            # CodeGenError 应该直接传播，不要包装
            from .codegen.errors import CodeGenError

            if isinstance(e, CodeGenError):
                raise
            # 其他异常包装为 RuntimeError
            raise RuntimeError(f"代码生成失败: {e}")

    def _handle_compilation_failure(self, error: Exception) -> CompilationResult:
        """处理整体编译失败的情况"""
        # 特殊处理严格类型检查错误
        from .passes.type_inference import StrictTypingError
        from .codegen.errors import CodeGenError

        if isinstance(error, StrictTypingError):
            # 返回独立的错误列表
            return CompilationResult(
                False,
                [],
                error.errors,
                generated_contents=None,
                ir_module=None,
            )

        # 特殊处理代码生成错误（不显示完整堆栈）
        if isinstance(error, CodeGenError):
            return CompilationResult(
                False,
                [],
                [str(error)],  # 只显示错误消息，不显示堆栈
                generated_contents=None,
                ir_module=None,
            )

        # 其他错误的通用处理
        import traceback

        error_msg = f"{str(error)}\n{traceback.format_exc()}"
        return CompilationResult(
            False,
            [],
            [error_msg],
            generated_contents=None,
            ir_module=None,
        )

    def _format_generated_contents(
        self, generated_files: Dict[str, str]
    ) -> Dict[str, str]:
        """使用 clang-format 在内存中格式化生成的源码内容。

        若 clang-format 不可用或发生错误，返回原内容。
        """
        formatted: Dict[str, str] = {}
        for name, content in generated_files.items():
            suffix = Path(name).suffix
            if suffix not in {".h", ".hpp", ".hh", ".hxx", ".cpp", ".cc", ".cxx"}:
                formatted[name] = content
                continue
            try:
                # 通过 stdin 传入源码，并使用 --assume-filename 让 clang-format 识别语言与风格文件
                proc = subprocess.run(
                    ["clang-format", "--assume-filename", name, "-style=file"],
                    input=content.encode("utf-8"),
                    capture_output=True,
                    check=True,
                )
                formatted[name] = proc.stdout.decode("utf-8") or content
            except (subprocess.CalledProcessError, FileNotFoundError):
                if self.config.get("verbose"):
                    print(f"警告: clang-format 不可用或失败，跳过内存格式化 {name}")
                formatted[name] = content
        return formatted

    def get_module_manager(self) -> ModuleManager:
        """获取模块管理器"""
        if self.module_manager is None:
            self.module_manager = ModuleManager(self)
        return self.module_manager
